生物学和计算机科学似乎是两个截然不同的领域,但是它们之间有一些惊人的相似之处。其中一个最显著的相似之处是它们的结构。生物细胞可以通过复制自身来产生另一个细胞,而计算机程序可以通过输出自身的源代码来“复制”自身。
一个 Quine 是一个计算机程序,它输出自己的源代码。在某些编程语言中,可以通过打开文件并打印其内容来实现 Quine,但是如果你的编程语言没有这个功能,该怎么办呢?你可以尝试在程序中包含自身的源代码,以字符串的形式。但是,如果你这样做,很快就会发现它会导致无限递归,并且无法正常工作。例如,下面这段 Python 代码就无法正常工作:
xxxxxxxxxx
11print(repr("print(repr('print(repr( ?????? ))'))"))
我们可以通过一些聪明的技巧来解决这个问题。例如,在 Python 中,我们可以使用字符串插值将一个字符串的内容插入到它自己的字符串中,然后打印它。这个程序就是一个 Quine,它的输出与它的源代码完全相同。下面是一个使用 Python 实现的 Quine:
xxxxxxxxxx
31x = """x = {}
2print(x.format('"'*3+x+'"'*3))"""
3print(x.format('"'*3+x+'"'*3))
实际上,只要你的编程语言是图灵完备的,就可以实现这个技巧。这个技巧看起来很奇怪,因为它似乎包含了相同的东西两次。这是 Quine 工作原理的基础:它们包含一个字符串形式的自身源代码,但是字符串并不包含自身的副本。相反,字符串被读取两次:一次作为字符串的副本,再次作为可执行代码的副本。
下面是 Go 语言实现的 一个Quine:
xxxxxxxxxx
121package main
2
3import "fmt"
4
5func main() {
6 s := "package main\n\nimport \"fmt\"\n\nfunc main() {\n s := %q\n fmt.Printf(s, s)\n}\n\nfunc hello() {\n\tfmt.Print(\"hello\")\n}\n"
7 fmt.Printf(s, s)
8}
9
10func hello() {
11 fmt.Print("hello")
12}
通过运行go run main.go >main2.go && go run main2.go >main3.go ...
得到的 main2.go,main3.go 跟 main.go 完全一样,就偈是 DNA 在复制自己一样。
我们可以将程序与 DNA 序列做类比,这样 ATCG 就像代码字符串,基因就像函数,DNA 序列的自我复制就是 DNA 里 main 基因的表达,DNA 里的其他基因则构成生物体的其他功能就像上面的 hello 函数一样。
这并不是一个新的发现,类似的想法可以在《哥德尔、埃舍尔、巴赫》中找到。事实上,现代计算机架构的发明者冯·诺伊曼的“通用构造器”细胞自动机就是基于同样的原理运作的。
生物DNA与程序之间的相似性为我们探索生命起源提供了启示。生命的起源可能与计算机程序的产生有些相似之处。我们可以将生命看作是一种自我复制的计算机程序,而生物细胞则是计算机中的运行时环境。当我们开始研究生命的本质时,我们可能会发现,生物学和计算机科学之间的界限并不像我们想象的那样清晰。
prompted by:
https://twitter.com/NathanielVirgo/status/1697492348421472729?s=20